
#ifndef __TINYPTC_TEXIMAGET__HEADER_FILE__
#define __TINYPTC_TEXIMAGET__HEADER_FILE__


#include "coid/comm/comm.h"

////////////////////////////////////////////////////////////////////////////////
static uint16 get_color555( uint32 c )
{
    //c += 0x00040404;
    return uint16( ((c&0x00f80000)>>9) | ((c&0x0000f800)>>6) | ((c&0x000000f8)>>3) );
}

static uint32 get_color32_555( uint16 c )
{
    return 0xff000000 | ((c&0x7c00)<<9) | ((c&0x03e0)<<6) | ((c&0x00f8)<<3);
}

////////////////////////////////////////////////////////////////////////////////
static uint16 get_color565( uint32 c )
{
    //c += 0x00040204;
    return uint16( ((c&0x00f80000)>>8) | ((c&0x0000fc00)>>5) | ((c&0x000000f8)>>3) );
}

static uint32 get_color32_565( uint16 c )
{
    return 0xff000000 | ((c&0xf800)<<8) | ((c&0x07e0)<<5) | ((c&0x00f8)<<3);
}


////////////////////////////////////////////////////////////////////////////////
template< int BPP>
struct uint_from_bpp
{
    typedef uint32  type_int;

    enum {
        xALPHA                  = 0xff000000,
        rALPHA                  = 32,
        xLSHIFT                 = 0xff7f7f7f,
    };

    static type_int pack_color( uint32 c )      { return c; }
    static uint32 unpack_color( type_int c )    { return c; }
};

template<> struct uint_from_bpp<24>
{
    typedef uint32  type_int;

    enum {
        xALPHA                  = 0x0000,
        rALPHA                  = 0,
        xLSHIFT                 = 0x007f7f7f,
    };

    static type_int pack_color( uint32 c )      { return c & 0x00ffffff; }
    static uint32 unpack_color( type_int c )    { return 0xff000000 | c; }
};

template<> struct uint_from_bpp<16>
{
    typedef uint16  type_int;

    enum {
        xALPHA                  = 0x0000,
        rALPHA                  = 0,
        xLSHIFT                 = 0x7bef,
    };

    static type_int pack_color( uint32 c )      { return get_color565(c); }
    static uint32 unpack_color( type_int c )    { return get_color32_565(c); }
};

template<> struct uint_from_bpp<15>
{
    typedef uint16  type_int;

    enum {
        xALPHA                  = 0x0000,
        rALPHA                  = 0,
        xLSHIFT                 = 0x3def,
    };

    static type_int pack_color( uint32 c )      { return get_color555(c); }
    static uint32 unpack_color( type_int c )    { return get_color32_555(c); }
};



////////////////////////////////////////////////////////////////////////////////
static inline uint32 scale_packed32( uint32 scale, uint32 p )
{
    ++scale;

	uint32 ag = (p & 0xFF00FF00) >> 8;
	uint32 rb =  p & 0x00FF00FF;

	uint32 sag = scale * ag;
	uint32 srb = scale * rb;

	sag = sag & 0xFF00FF00;
	srb = (srb >> 8) & 0x00FF00FF;

	return sag | srb;
}

////////////////////////////////////////////////////////////////////////////////
static inline uint32 scale_packed32_rgb( uint32 scale, uint32 p )
{
    ++scale;

	uint32 ag = (p & 0x0000FF00) >> 8;
	uint32 rb =  p & 0x00FF00FF;

	uint32 sag = scale * ag;
	uint32 srb = scale * rb;

	sag = (sag & 0x0000FF00) | (p & 0xff000000);
	srb = (srb >> 8) & 0x00FF00FF;

	return sag | srb;
}

////////////////////////////////////////////////////////////////////////////////
static inline uint32 lerp_packed32( uint32 a, uint32 d, uint32 s )
{
    ++a;

	uint32 dstrb = d      & 0xFF00FF;
	uint32 dstag = d >> 8 & 0xFF00FF;

	uint32 srcrb = s      & 0xFF00FF;
	uint32 srcag = s >> 8 & 0xFF00FF;

	uint32 drb = srcrb - dstrb;
	uint32 dag = srcag - dstag;

	drb *= a;  
	dag *= a;  
	drb >>= 8;
	dag >>= 8;

	const uint32 rb  = (drb + dstrb)      & 0x00FF00FF;
	const uint32 ag  = (dag + dstag) << 8 & 0xFF00FF00;

	return rb | ag;
}

////////////////////////////////////////////////////////////////////////////////
static inline uint32 blend_packed32_rgb( uint32 a, uint32 d, uint32 s )
{
	++a;

	const uint32 dstrb = d & 0xFF00FF;
	const uint32 dstg  = d & 0xFF00;

	const uint32 srcrb = s & 0xFF00FF;
	const uint32 srcg  = s & 0xFF00;

	uint32 drb = srcrb - dstrb;
	uint32 dg  =  srcg - dstg;

	drb *= a;
	dg  *= a;  
	drb >>= 8;
	dg  >>= 8;

	uint32 rb = (drb + dstrb) & 0xFF00FF;
	uint32 g  = (dg  + dstg) & 0xFF00;

	return rb | g;
}

////////////////////////////////////////////////////////////////////////////////
struct Drawer
{
    typedef void (*fnc_draw_line) ( const uint8* src, uint8* dst, uint len, int step, void* ctx );
    typedef uint32 (*fnc_unpack_color) ( const void* );
    typedef void (*fnc_pack_color) ( uint32, void* );

    struct LineDrawers
    {
        fnc_draw_line   _draw_line;
        fnc_draw_line   _draw_line_ak;
        fnc_draw_line   _draw_line_a;
        fnc_draw_line   _draw_line_s;
        fnc_draw_line   _draw_line_c;
        fnc_draw_line   _draw_line_cak;
        fnc_draw_line   _draw_line_ca;
    };

    fnc_pack_color      _pack_color;
    fnc_unpack_color    _unpack_color;

    LineDrawers _draw;
    LineDrawers _draw32;
};


////////////////////////////////////////////////////////////////////////////////
template< int BPP >
struct DrawerT : public Drawer
{
    typedef uint_from_bpp<BPP>                      elemT;
    typedef typename uint_from_bpp<BPP>::type_int   uintT;

    static uint32 unpack_color( const void* c )     { return elemT::unpack_color( *(const uintT*)c ); }
    static void pack_color( uint32 c, void* d )     { *(uintT*)d = elemT::pack_color(c); }

    DrawerT()
    {
        _pack_color             = &pack_color;
        _unpack_color           = &unpack_color;

        _draw._draw_line        = &draw_line;
        _draw._draw_line_ak     = &draw_line_ak;
        _draw._draw_line_a      = &draw_line_a;
        _draw._draw_line_s      = &draw_line_s;
        _draw._draw_line_c      = &draw_line_cak;
        _draw._draw_line_cak    = &draw_line_cak;
        _draw._draw_line_ca     = &draw_line_ca;

        _draw32._draw_line      = BPP==32 ? &draw_line : &draw_line32;
        _draw32._draw_line_ak   = BPP==32 ? &draw_line_ak : &draw_line_ak32;
        _draw32._draw_line_a    = BPP==32 ? &draw_line_a : &draw_line_a32;
        _draw32._draw_line_s    = BPP==32 ? &draw_line_s : &draw_line_s32;
        _draw32._draw_line_c    = BPP==32 ? &draw_line_cak : 0;
        _draw32._draw_line_cak  = BPP==32 ? &draw_line_cak : 0;
        _draw32._draw_line_ca   = BPP==32 ? &draw_line_ca : 0;
    }

    ////////////////////////////////////////////////////////////////////////////////
    ///blit
    static void draw_line( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        if( step == sizeof(uintT) )
            xmemcpy( dst, src, len*sizeof(uintT) );
        else
        {
            for( uint i=0; i<len; ++i,src+=step )
                ((uintT*)dst)[i] = *(const uintT*)src;
        }
    }

    ///blit from 32
    static void draw_line32( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
            ((uintT*)dst)[i] = elemT::pack_color(*(const uint32*)src);
    }

    ///alpha blit
    static void draw_line_a( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
        {
            int sh = elemT::rALPHA-8;
            if( sh <= 0 ) continue;

            int a = (*(const uintT*)src & elemT::xALPHA) >> sh;
            if( a == 0 )  continue;
/*
            //unoptimized version
            int ia = 0xff-a;
            dstb[0] = ((srcb[0]*a)>>8) + ((dstb[0]*ia)>>8);
            dstb[1] = ((srcb[1]*a)>>8) + ((dstb[1]*ia)>>8);
            dstb[2] = ((srcb[2]*a)>>8) + ((dstb[2]*ia)>>8);
*/
            ((uintT*)dst)[i] = *(const uintT*)src + (uintT)scale_packed32( 0xff-a, ((const uintT*)dst)[i] );
        }
    }

    ///alpha blit from 32
    static void draw_line_a32( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
        {
            int a = (*(const uint32*)src & uint_from_bpp<32>::xALPHA) >> (uint_from_bpp<32>::rALPHA-8);
            if( a == 0 )  continue;

            ((uintT*)dst)[i] = elemT::pack_color( *(const uint32*)src + scale_packed32( 0xff-a, elemT::unpack_color(((const uintT*)dst)[i]) ) );
        }
    }

    ///alpha keyed blit
    static void draw_line_ak( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
            if( *(const uintT*)src & elemT::xALPHA )
                ((uintT*)dst)[i] = *(const uintT*)src;
    }

    ///alpha keyed blit from 32
    static void draw_line_ak32( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
            if( *(const uint32*)src & uint_from_bpp<32>::xALPHA )
                ((uintT*)dst)[i] = elemT::pack_color(*(const uint32*)src);
    }

    ///shadow blit
    static void draw_line_s( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
            if( *(const uintT*)src & elemT::xALPHA )
                ((uintT*)dst)[i] = (((uintT*)dst)[i] >> 1) & elemT::xLSHIFT;
    }

    ///shadow blit from 32
    static void draw_line_s32( const uint8* src, uint8* dst, uint len, int step, void* )
    {
        for( uint i=0; i<len; ++i,src+=step )
            if( *(const uint32*)src & uint_from_bpp<32>::xALPHA )
                ((uintT*)dst)[i] = (((uintT*)dst)[i] >> 1) & elemT::xLSHIFT;
    }

    ///alpha keyed blit with colorization
    static void draw_line_cak( const uint8* src, uint8* dst, uint len, int step, void* ctx )
    {
        for( uint i=0; i<len; ++i,src+=step )
        {
            if( (*(const uintT*)src & elemT::xALPHA) == 0 )  continue;
/*
            int dg = src[1] - src[2];
            int dr = src[0] - src[2];
            if( dg >= dr )
            {
                dst[0] = src[2] + ((cpr*dg)>>8);
                dst[1] = src[2] + ((cpg*dg)>>8);
                dst[2] = src[2] + ((cpb*dg)>>8);
            }
            else if(dr)
            {
                dst[0] = src[2] + ((csr*dr)>>8);
                dst[1] = src[2] + ((csg*dr)>>8);
                dst[2] = src[2] + ((csb*dr)>>8);
            }
*/
            uint32 bri = src[2];
            int dg = src[1] - bri;
            int dr = src[0] - bri;
            bri += bri << 8;
            bri += bri << 16;

            if( dg >= dr )
                ((uintT*)dst)[i] = uintT(bri + scale_packed32( dg, ((uint32*)ctx)[0] ) );
            else
                ((uintT*)dst)[i] = uintT(bri + scale_packed32( dr, ((uint32*)ctx)[1] ) );
        }
	}

    ///alpha blit with colorization
    static void draw_line_ca( const uint8* src, uint8* dst, uint len, int step, void* ctx )
    {
        for( uint i=0; i<len; ++i,src+=step )
        {
            int sh = elemT::rALPHA-8;
            if( sh <= 0 ) continue;

            int a = (*(const uintT*)src & elemT::xALPHA) >> sh;
            if( a == 0 )  continue;

            uint32 bri = src[2];
            int dg = src[1] - bri;
            int dr = src[0] - bri;
            bri += bri << 8;
            bri += bri << 16;

            uint c;
            if( dg >= dr )
                c = bri + scale_packed32( dg, ((uint32*)ctx)[0] );
            else
                c = bri + scale_packed32( dr, ((uint32*)ctx)[1] );

            ((uintT*)dst)[i] = c + (uintT)scale_packed32( 0xff-a, ((const uintT*)dst)[i] );
        }
	}

};

#endif //__TINYPTC_TEXIMAGET__HEADER_FILE__
